package com.woohua.thread.product;

import java.util.Vector;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/***
 * @title BlockedQueue
 * @description：阻塞任务队列，添加任务时如果已经达到容量上限，则会阻塞等待
 * @author woo hua
 * @version 1.0.0
 * @create 2023/3/14 11:01
 **/
public class BlockedQueue<T> {

    // 锁
    private final Lock lock = new ReentrantLock();

    // 条件变量：队列不满
    private final Condition notFull = lock.newCondition();

    // 条件不变：队列不空
    private final Condition notEmpty = lock.newCondition();

    // 任务集合
    private Vector<T> taskQueue = new Vector<>();

    // 队列容量
    private final int capacity;

    /**
     * 构造器
     *
     * @param capacity 队列容量
     */
    public BlockedQueue(int capacity) {
        this.capacity = capacity;
    }

    /**
     * 入队操作
     *
     * @param t
     */
    public void enq(T t) {
        lock.lock();
        try {
            System.out.println("size: " + taskQueue.size() + " capacity: " + capacity);
            while (taskQueue.size() == this.capacity) {
                // 队列满了之后等待，等待队列不满
                notFull.await();
            }

            System.out.println(Thread.currentThread().getName() + " add task: " + t.toString());
            taskQueue.add(t);

            // 如队后，通知队列不空了，可以出队
            notEmpty.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 出队操作
     *
     * @return
     */
    public T deq() {
        lock.lock();
        try {

            while (taskQueue.size() == 0) {
                try {
                    // 队列为空时等待，等待队列不空
                    notEmpty.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            T t = taskQueue.remove(0);

            // 出队后，通知队列不满，可以继续入队
            notFull.signal();

            return t;
        } finally {
            lock.unlock();
        }
    }
}
